package share.completion;

import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.Date;
import java.util.concurrent.*;

import static share.completion.CompletionServiceTest.*;

/**
 * @author 小石潭记
 * @date 2021/4/19 20:32
 * @Description: 还在使用Future轮询获取结果吗？CompletionService快来了解下。
 *  https://mp.weixin.qq.com/s/Z_Buz-Uiez41SXz7zpT3LQ
 */
public class CompletionServiceTest3 {

    final static ExecutorService executor = new ThreadPoolExecutor(6, 6,
            0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue<>());

    public static void main(String[] args) throws ExecutionException, InterruptedException {
        // 将新异步任务的生成与已完成任务的结果的使用分离的服务。生产者{@code submit}任务执行。
        // 使用者{@code take}完成任务，并按完成的顺序处理结果。

        // CompletionService实现了生产者提交任务和消费者获取结果的解耦，生产者和消费者都不用关心任务
        // 的完成顺序，由CompletionService来保证，消费者一定是按照任务完成的先后顺序来获取执行结果。
        CompletionService completionService = new ExecutorCompletionService(executor);
        System.out.println(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss").format(LocalDateTime.now()) + "---" + "开始----");
        // ExecutorCompletionService任务的提交和执行都是委托给Executor来完成。
        // 当提交某个任务时，该任务首先将被包装为一个QueueingFuture
        completionService.submit(() -> getMouZhuFlightPrice()); // 178 line
        completionService.submit(() -> getMouXieFlightPrice());
        completionService.submit(() -> getMouTuanFlightPrice());
        for (int i = 0; i < 3; i++) {
            // take和poll都是调用BlockingQueue提供的方法。
            //take() 获取任务阻塞，直到可以拿到任务为止。
            //poll() 获取任务不阻塞，如果没有获取到任务直接返回null。
            //poll(long timeout, TimeUnit unit) 带超时时间等待的获取任务方法（一般推荐使用这种）
            String result = (String)completionService.take().get();
            System.out.println(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss").format(LocalDateTime.now()) + "---" + result);
            saveDb(result);
        }

        /*public class ExecutorCompletionService<V> implements CompletionService<V> {
        // 执行task的线程池，创建CompletionService必须指定；
        private final Executor executor;
        //主要用于创建待执行task；
        private final AbstractExecutorService aes;
        //存储已完成状态的task，默认是基于链表结构的阻塞队列LinkedBlockingQueue。
        private final BlockingQueue<Future<V>> completionQueue;*/

        // 从源码可以看出，QueueingFuture是FutureTask的子类，实现了done方法，
        // 在task执行完成之后将当前task添加到completionQueue，
        // 将返回结果加入到阻塞队列中，加入的顺序就是任务完成的先后顺序。
        // done方法的具体调用在FutureTask的finishCompletion方法。

//        总结
//        CompletionService 把线程池 Executor 和阻塞队列 BlockingQueue融合在一起，能够让批异步任务的管理更简单，将生产者提交任务和消费者获取结果的解耦。
//        CompletionService 能够让异步任务的执行结果有序化，先执行完的先进入阻塞队列，利用这个特性，我们可以轻松实现后续处理的有序性，避免无谓的等待。

    }

}